Passed
Pull Request — develop (#256)
by Xaver
01:38
created

labellayer.js ➔ define   B

Complexity

Conditions 1
Paths 2

Size

Total Lines 351

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
c 3
b 0
f 0
nc 2
dl 0
loc 351
rs 7
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
define(['leaflet', 'rbush', 'helper', 'moment'],
2
  function (L, rbush, helper, moment) {
3
    'use strict';
4
5
    var groupOnline;
6
    var groupOffline;
7
    var groupNew;
8
    var groupLost;
9
    var groupLines;
10
11
    var labelLocations = [['left', 'middle', 0 / 8],
12
      ['center', 'top', 6 / 8],
13
      ['right', 'middle', 4 / 8],
14
      ['left', 'top', 7 / 8],
15
      ['left', 'ideographic', 1 / 8],
16
      ['right', 'top', 5 / 8],
17
      ['center', 'ideographic', 2 / 8],
18
      ['right', 'ideographic', 3 / 8]];
19
    var labelShadow;
20
    var bodyStyle = { fontFamily: 'sans-serif' };
21
    var nodeRadius = 4;
22
23
    var cFont = document.createElement('canvas').getContext('2d');
24
25
    function measureText(font, text) {
26
      cFont.font = font;
27
      return cFont.measureText(text);
28
    }
29
30
    function mapRTree(d) {
31
      return { minX: d.position.lat, minY: d.position.lng, maxX: d.position.lat, maxY: d.position.lng, label: d };
32
    }
33
34
    function prepareLabel(fillStyle, fontSize, offset, stroke) {
35
      return function (d) {
36
        var font = fontSize + 'px ' + bodyStyle.fontFamily;
37
        return {
38
          position: L.latLng(d.location.latitude, d.location.longitude),
39
          label: d.hostname,
40
          offset: offset,
41
          fillStyle: fillStyle,
42
          height: fontSize * 1.2,
43
          font: font,
44
          stroke: stroke,
45
          width: measureText(font, d.hostname).width
46
        };
47
      };
48
    }
49
50
    function calcOffset(offset, loc) {
51
      return [offset * Math.cos(loc[2] * 2 * Math.PI),
52
        offset * Math.sin(loc[2] * 2 * Math.PI)];
53
    }
54
55
    function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) {
56
      var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom));
57
58
      var width = label.width * margin;
59
      var height = label.height * margin;
60
61
      var dx = {
62
        left: 0,
63
        right: -width,
64
        center: -width / 2
65
      };
66
67
      var dy = {
68
        top: 0,
69
        ideographic: -height,
70
        middle: -height / 2
71
      };
72
73
      var x = p.x + offset[0] + dx[anchor[0]];
74
      var y = p.y + offset[1] + dy[anchor[1]];
75
76
      return { minX: x, minY: y, maxX: x + width, maxY: y + height };
77
    }
78
79
    function mkMarker(dict, iconFunc) {
80
      return function (d) {
81
        var m = L.circleMarker([d.location.latitude, d.location.longitude], iconFunc(d));
82
83
        m.resetStyle = function resetStyle() {
84
          m.setStyle(iconFunc(d));
85
        };
86
87
        m.on('click', function () {
88
          router.fullUrl({ node: d.node_id });
89
        });
90
        m.bindTooltip(d.hostname);
91
92
        dict[d.node_id] = m;
93
94
        return m;
95
      };
96
    }
97
98
    function addLinksToMap(dict, linkScale, graph) {
99
      graph = graph.filter(function (d) {
100
        return 'distance' in d && d.type.indexOf('vpn') !== 0;
101
      });
102
103
      return graph.map(function (d) {
104
        var opts = {
105
          color: linkScale((d.source_tq + d.target_tq) / 2),
106
          weight: 4,
107
          opacity: 0.5,
108
          dashArray: 'none'
109
        };
110
111
        var line = L.polyline(d.latlngs, opts);
112
113
        line.resetStyle = function resetStyle() {
114
          line.setStyle(opts);
115
        };
116
117
        line.bindTooltip(d.source.hostname + ' – ' + d.target.hostname +
118
          '<br><strong>' + helper.showDistance(d) + ' / ' + helper.showTq(d.source_tq) + ' - ' + helper.showTq(d.target_tq) + '<br>' + d.type + '</strong>');
119
120
        line.on('click', function () {
121
          router.fullUrl({ link: d.id });
122
        });
123
124
        dict[d.id] = line;
125
126
        return line;
127
      });
128
    }
129
130
    function getIcon(color) {
131
      return Object.assign({}, config.icon.base, config.icon[color]);
132
    }
133
134
    return L.GridLayer.extend({
135
      onAdd: function (map) {
136
        L.GridLayer.prototype.onAdd.call(this, map);
137
        if (this.data) {
138
          this.prepareLabels();
139
        }
140
      },
141
      setData: function (data, map, nodeDict, linkDict, linkScale) {
142
        var iconOnline = getIcon('online');
143
        var iconOffline = getIcon('offline');
144
        var iconLost = getIcon('lost');
145
        var iconAlert = getIcon('alert');
146
        var iconNew = getIcon('new');
147
        // Check if init or data is already set
148
        if (groupLines) {
149
          groupOffline.clearLayers();
150
          groupOnline.clearLayers();
151
          groupNew.clearLayers();
152
          groupLost.clearLayers();
153
          groupLines.clearLayers();
154
        }
155
156
        var lines = addLinksToMap(linkDict, linkScale, data.links);
157
        groupLines = L.featureGroup(lines).addTo(map);
158
159
        var nodesOnline = helper.subtract(data.nodes.online, data.nodes.new).filter(helper.hasLocation);
160
        var nodesOffline = helper.subtract(data.nodes.offline, data.nodes.lost).filter(helper.hasLocation);
161
        var nodesNew = data.nodes.new.filter(helper.hasLocation);
162
        var nodesLost = data.nodes.lost.filter(helper.hasLocation);
163
164
        var markersOnline = nodesOnline.map(mkMarker(nodeDict, function () {
165
          return iconOnline;
166
        }));
167
168
        var markersOffline = nodesOffline.map(mkMarker(nodeDict, function () {
169
          return iconOffline;
170
        }));
171
172
        var markersNew = nodesNew.map(mkMarker(nodeDict, function () {
173
          return iconNew;
174
        }));
175
176
        var markersLost = nodesLost.map(mkMarker(nodeDict, function (d) {
177
          var age = moment(data.now).diff(d.lastseen, 'days', true);
178
          if (age <= config.maxAgeAlert) {
179
            return iconAlert;
180
          }
181
          if (age <= config.maxAge) {
182
            return iconLost;
183
          }
184
          return null;
185
        }));
186
187
        groupOffline = L.featureGroup(markersOffline).addTo(map);
188
        groupLost = L.featureGroup(markersLost).addTo(map);
189
        groupOnline = L.featureGroup(markersOnline).addTo(map);
190
        groupNew = L.featureGroup(markersNew).addTo(map);
191
192
        this.data = {
193
          online: nodesOnline,
194
          offline: nodesOffline,
195
          new: nodesNew,
196
          lost: nodesLost
197
        };
198
        this.updateLayer();
199
      },
200
      updateLayer: function () {
201
        if (this._map) {
202
          this.prepareLabels();
203
        }
204
      },
205
      prepareLabels: function () {
206
        var d = this.data;
207
208
        // label:
209
        // - position (WGS84 coords)
210
        // - offset (2D vector in pixels)
211
        // - anchor (tuple, textAlignment, textBaseline)
212
        // - minZoom (inclusive)
213
        // - label (string)
214
        // - color (string)
215
216
        var labelsOnline = d.online.map(prepareLabel(null, 11, 8, true));
217
        var labelsOffline = d.offline.map(prepareLabel(config.icon.offline.color, 9, 5, false));
218
        var labelsNew = d.new.map(prepareLabel(config.map.labelNewColor, 11, 8, true));
219
        var labelsLost = d.lost.map(prepareLabel(config.icon.lost.color, 11, 8, true));
220
221
        var labels = []
222
          .concat(labelsNew)
223
          .concat(labelsLost)
224
          .concat(labelsOnline)
225
          .concat(labelsOffline);
226
227
        var minZoom = this.options.minZoom;
228
        var maxZoom = this.options.maxZoom;
229
230
        var trees = [];
231
232
        var map = this._map;
233
234
        function nodeToRect(z) {
235
          return function (n) {
236
            var p = map.project(n.position, z);
237
            return { minX: p.x - nodeRadius, minY: p.y - nodeRadius, maxX: p.x + nodeRadius, maxY: p.y + nodeRadius };
238
          };
239
        }
240
241
        for (var z = minZoom; z <= maxZoom; z++) {
242
          trees[z] = rbush(9);
243
          trees[z].load(labels.map(nodeToRect(z)));
244
        }
245
246
        labels = labels.map(function (n) {
247
          var best = labelLocations.map(function (loc) {
248
            var offset = calcOffset(n.offset, loc);
249
            var i;
250
251
            for (i = maxZoom; i >= minZoom; i--) {
252
              var p = map.project(n.position, i);
253
              var rect = labelRect(p, offset, loc, n, minZoom, maxZoom, i);
254
              var candidates = trees[i].search(rect);
255
256
              if (candidates.length > 0) {
257
                break;
258
              }
259
            }
260
261
            return { loc: loc, z: i + 1 };
262
          }).filter(function (k) {
263
            return k.z <= maxZoom;
264
          }).sort(function (a, b) {
265
            return a.z - b.z;
266
          })[0];
267
268
          if (best !== undefined) {
269
            n.offset = calcOffset(n.offset, best.loc);
270
            n.minZoom = best.z;
271
            n.anchor = best.loc;
272
273
            for (var i = maxZoom; i >= best.z; i--) {
274
              var p = map.project(n.position, i);
275
              var rect = labelRect(p, n.offset, best.loc, n, minZoom, maxZoom, i);
276
              trees[i].insert(rect);
277
            }
278
279
            return n;
280
          }
281
          return undefined;
282
        }).filter(function (n) {
283
          return n !== undefined;
284
        });
285
286
        this.margin = 16;
287
288
        if (labels.length > 0) {
289
          this.margin += labels.map(function (n) {
290
            return n.width;
291
          }).sort().reverse()[0];
292
        }
293
294
        this.labels = rbush(9);
295
        this.labels.load(labels.map(mapRTree));
296
297
        this.redraw();
298
      },
299
      createTile: function (tilePoint) {
300
        var tile = L.DomUtil.create('canvas', 'leaflet-tile');
301
302
        var tileSize = this.options.tileSize;
303
        tile.width = tileSize;
304
        tile.height = tileSize;
305
306
        if (!this.labels) {
307
          return tile;
308
        }
309
310
        var s = tilePoint.multiplyBy(tileSize);
311
        var map = this._map;
312
        bodyStyle = window.getComputedStyle(document.querySelector('body'));
313
        labelShadow = bodyStyle.backgroundColor.replace(/rgb/i, 'rgba').replace(/\)/i, ',0.7)');
314
315
        function projectNodes(d) {
316
          var p = map.project(d.label.position);
317
318
          p.x -= s.x;
319
          p.y -= s.y;
320
321
          return { p: p, label: d.label };
322
        }
323
324
        var bbox = helper.getTileBBox(s, map, tileSize, this.margin);
325
        var labels = this.labels.search(bbox).map(projectNodes);
326
        var ctx = tile.getContext('2d');
327
328
        ctx.lineWidth = 5;
329
        ctx.strokeStyle = labelShadow;
330
        ctx.miterLimit = 2;
331
332
        function drawLabel(d) {
333
          ctx.font = d.label.font;
334
          ctx.textAlign = d.label.anchor[0];
335
          ctx.textBaseline = d.label.anchor[1];
336
          ctx.fillStyle = d.label.fillStyle === null ? bodyStyle.color : d.label.fillStyle;
337
338
          if (d.label.stroke) {
339
            ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]);
340
          }
341
342
          ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]);
343
        }
344
345
        labels.filter(function (d) {
346
          return tilePoint.z >= d.label.minZoom;
347
        }).forEach(drawLabel);
348
349
        return tile;
350
      }
351
    });
352
  });
353